import SwiftUI
import CalendarShared
import AppIntents

public struct MonthView: View {
    public enum Style {
        case small((any AppIntent)?, (any AppIntent)?, (any AppIntent)?)
        case large((any AppIntent)?, (any AppIntent)?, (any AppIntent)?)
    }

    public var style: Style
    public var entry: LargeEntry
    @Environment(\.widgetRenderingMode) var renderingMode
    @Environment(\.colorScheme) var colorScheme

    public init(style: Style, entry: LargeEntry) {
        self.style = style
        self.entry = entry
    }

    public var body: some View {
        switch style {
        case .small(let today, let last, let next): smallMonthView(todyIntent: today, lastIntent: last, nextIntent: next)
        case .large(let today, let last, let next): largeMonthView(todyIntent: today, lastIntent: last, nextIntent: next)
        }
    }
}

private extension MonthView {
    struct Constants {
        static let smallHeaderHeight: CGFloat = 14
        static let smallWeekHeight: CGFloat = 14
        static let largeHeaderHeight: CGFloat = 26
        static let largeWeekHeight: CGFloat = 24
    }

    func smallMonthView(todyIntent: (any AppIntent)?, lastIntent: (any AppIntent)?, nextIntent: (any AppIntent)?) -> some View {
        GeometryReader { geometry in
            ZStack {
                VStack(spacing: 4) {
                    smallHeader
                    smallWeekView(geometry)
                    smallDateView(geometry)
                }
                HStack(spacing: 0) {
                    #if os(iOS)
                    if #available(iOS 17.0, *) {
                        IntentButton(intent: lastIntent) {
                            Color.clear
                        }
                        IntentButton(intent: todyIntent) {
                            Color.clear
                        }
                        IntentButton(intent: nextIntent) {
                            Color.clear
                        }
                    }
                    #else
                    if #available(macOS 14.0, *) {
                        IntentButton(intent: lastIntent) {
                            Color.clear
                        }
                        IntentButton(intent: todyIntent) {
                            Color.clear
                        }
                        IntentButton(intent: nextIntent) {
                            Color.clear
                        }
                    }
                    #endif
                }
            }
        }
    }

    var smallHeader: some View {
        let month = NSLocalizedString("BearCalendar.calendar.month", bundle: moduleBundle, comment: "")
        let format = entry.language.isEnglish ? "MMM yyyy" : "M\(month) yyyy"
        let title = entry.date.formated(.other(format), language: entry.language)
        return HStack(spacing: 5) {
            Spacer()
            Text(title)
                .font(.system(size: 14))
                .foreground(color: entry.normal.color(colorScheme))
                .widgetAccentable(renderingMode == .accented)
            Spacer()
        }
        .frame(height: Constants.smallHeaderHeight, alignment: .center)
        .padding(.horizontal, 0)
    }

    func smallWeekView(_ geometry: GeometryProxy) -> some View {
        LazyVGrid(columns: Array(repeating: GridItem(spacing: 5), count: 7), spacing: 5) {
            let itemW = (geometry.size.width - 10) / 7
            let itemH = Constants.smallWeekHeight
            ForEach(Week.weeks(beginAt: entry.firstWeek), id: \.self) { week in
                Button {} label: {
                    Text(week.title(entry.language))
                        .font(entry.language.isEnglish ? .system(size: 7) : .system(size: 10))
                        .foreground(color: weekColor(week))
                        .widgetAccentable(renderingMode == .accented)
                }
                .buttonStyle(.plain)
                .frame(width: itemW, height: itemH, alignment: .center)
            }
        }
        .padding(.horizontal, 0)
    }

    func smallDateView(_ geometry: GeometryProxy) -> some View {
        LazyVGrid(columns:Array(repeating: GridItem(spacing: 4), count: 7), spacing: 4) {
            let row = entry.month.days.count / 7
            let itemW = (geometry.size.width - 10) / 7
            let spacing = (row + 1) * 4
            let itemH = (geometry.size.height - Constants.smallHeaderHeight - Constants.smallWeekHeight - CGFloat(spacing)) / CGFloat(row)
            let raidus = min(itemW, itemH) / 2.0
            ForEach(entry.month.days) { entity in
                Group {
                    Text("\(entity.date.day)")
                        .font(.system(size: 8))
                        .foreground(color: dateColor(entity))
                        .widgetAccentable(renderingMode == .accented)
                }
                .frame(width: itemW, height: itemH, alignment: .center)
                .background(RoundedRectangle(cornerRadius: raidus).stroke(entity.date.isCurrentDay ? entry.current.color(colorScheme) : .clear))
            }
        }
    }
}

private extension MonthView {
    func largeMonthView(todyIntent: (any AppIntent)?, lastIntent: (any AppIntent)?, nextIntent: (any AppIntent)?) -> some View {
        GeometryReader { geometry in
            VStack(spacing: 0) {
                largeHeader(todyIntent: todyIntent, lastIntent: lastIntent, nextIntent: nextIntent)
                largeWeekView(geometry)
                largeDateView(geometry)
            }
        }
    }

    func largeHeader(todyIntent: (any AppIntent)?, lastIntent: (any AppIntent)?, nextIntent: (any AppIntent)?) -> some View {
        let month = NSLocalizedString("BearCalendar.calendar.month", bundle: moduleBundle, comment: "")
        let format = entry.language.isEnglish ? "MMM yyyy" : "M\(month) yyyy"
        let title = entry.date.formated(.other(format), language: entry.language)
        return HStack(spacing: 20) {
            Spacer()
            #if os(iOS)
            if #available(iOS 17.0, *) {
                IntentButton(intent: lastIntent) {
                    Image(systemName: "chevron.left")
                        .resizable()
                        .scaledToFill()
                        .frame(width: 13, height: 13)
                        .foreground(color: entry.normal.color(colorScheme))
                }
                IntentButton(intent: todyIntent) {
                    Text(title)
                        .font(.title2)
                        .foreground(color: entry.normal.color(colorScheme))
                        .widgetAccentable(renderingMode == .accented)
                }
                IntentButton(intent: nextIntent) {
                    Image(systemName: "chevron.right")
                        .resizable()
                        .scaledToFill()
                        .frame(width: 13, height: 13)
                        .foreground(color: entry.normal.color(colorScheme))
                }
            } else {
                Text(title)
                    .font(.title2)
                    .foreground(color: entry.normal.color(colorScheme))
                    .widgetAccentable(renderingMode == .accented)
            }
            #else
            if #available(macOS 14.0, *) {
                IntentButton(intent: lastIntent) {
                    Image(systemName: "chevron.left")
                        .resizable()
                        .scaledToFill()
                        .frame(width: 13, height: 13)
                        .foreground(color: entry.normal.color(colorScheme))
                }
                IntentButton(intent: todyIntent) {
                    Text(title)
                        .font(.title2)
                        .foreground(color: entry.normal.color(colorScheme))
                }
                IntentButton(intent: nextIntent) {
                    Image(systemName: "chevron.right")
                        .resizable()
                        .scaledToFill()
                        .frame(width: 13, height: 13)
                        .foreground(color: entry.normal.color(colorScheme))
                }
            } else {
                Text(title)
                    .font(.title2)
                    .foreground(color: entry.normal.color(colorScheme))
                    .widgetAccentable(renderingMode == .accented)
            }
            #endif
            Spacer()
        }
        .frame(height: Constants.largeHeaderHeight, alignment: .center)
        .padding(.horizontal, 15)
    }

    func largeWeekView(_ geometry: GeometryProxy) -> some View {
        LazyVGrid(columns: Array(repeating: GridItem(spacing: 5), count: 7), spacing: 5) {
            let itemW = (geometry.size.width - 40) / 7
            let itemH = Constants.largeWeekHeight
            ForEach(Week.weeks(beginAt: entry.firstWeek), id: \.self) { week in
                Button {} label: {
                    Text(week.title(entry.language))
                        .font(.body)
                        .foreground(color: weekColor(week))
                        .widgetAccentable(renderingMode == .accented)
                }
                .buttonStyle(.plain)
                .frame(width: itemW, height: itemH, alignment: .center)
            }
        }
        .padding(.horizontal, 5)
    }

    func largeDateView(_ geometry: GeometryProxy) -> some View {
        LazyVGrid(columns: Array(repeating: GridItem(spacing: 4), count: 7), spacing: 4) {
            let row = entry.month.days.count / 7
            let itemW = (geometry.size.width - 40) / 7
            let spacing = (row + 1) * 4
            let itemH = (geometry.size.height - Constants.largeHeaderHeight - Constants.largeWeekHeight - CGFloat(spacing)) / CGFloat(row)
            ForEach(entry.month.days, id: \.id) { entity in
                Button {} label: {
                    VStack(spacing: 0) {
                        HStack(spacing: 4) {
                            Text("\(entity.date.day)")
                                .font(.system(size: 14).bold())
                                .foreground(color: dateColor(entity))
                                .widgetAccentable(renderingMode == .accented)
                            if entity.tiaoxiu != .none, entry.showFestival {
                                Image(entity.tiaoxiu == .work ? "work" : "rest")
                                    .resizable()
                                    .frame(width: 10, height: 11)
                                    .foreground(color: largeWorkColor(entity))
                            }
                        }
                        .padding(.top, 3)
                        if entry.showLunar {
                            Text(entity.lunar)
                                .font(.system(size: 10))
                                .foreground(color: largeFestivalColor(entity))
                                .widgetAccentable(renderingMode == .accented)
                                .padding(.bottom, 3)
                        }
                        largeEventView(entity)
                    }
                }
                .buttonStyle(.plain)
                .frame(width: itemW, height: itemH, alignment: .center)
                .background(RoundedRectangle(cornerRadius: 8).stroke(entity.date.isCurrentDay ? entry.current.color(colorScheme) : .clear))
            }
        }.padding(.all, 5)
    }

    @ViewBuilder
    func largeEventView(_ model: DayModel) -> some View {
        if entry.showEvent, model.events.count > 0, entry.showReminder, model.reminders.count > 0 {
            HStack(spacing: 2) {
                entry.calendar.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1).cornerRadius(2).frame(width: 4, height: 4)
                entry.reminder.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1).cornerRadius(2).frame(width: 4, height: 4)
            }
        } else if entry.showEvent, model.events.count > 0 {
            entry.calendar.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1).cornerRadius(2).frame(width: 4, height: 4)
        } else if entry.showReminder, model.reminders.count > 0 {
            entry.reminder.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1).cornerRadius(2).frame(width: 4, height: 4)
        } else {
            EmptyView()
        }
    }

    func largeFestivalColor(_ model: DayModel) -> Color {
        // 节假日
        if model.isFestival {
            return entry.festival.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
        }
        // 周末
        if model.date.isWeekDay {
            return entry.week.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
        }
        // 一般日期
        return entry.normal.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
    }

    func largeWorkColor(_ model: DayModel) -> Color {
        switch model.tiaoxiu {
        case .work: entry.work.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
        case .rest: entry.rest.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
        case .none: entry.normal.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
        }
    }
}

private extension MonthView {
    func weekColor(_ week: Week) -> Color {
        if week == .sun || week == .sat {
            entry.week.color(colorScheme)
        } else {
            entry.normal.color(colorScheme)
        }
    }

    func dateColor(_ model: DayModel) -> Color {
        // 调休
        if model.tiaoxiu != .none {
            return switch model.tiaoxiu {
                case .work: entry.work.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
                case .rest: entry.rest.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
                case .none: entry.normal.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
            }
        }
        // 节假日
        if model.isFestival {
            return entry.festival.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
        }
        // 周末
        if model.date.isWeekDay {
            return entry.week.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
        }
        // 一般日期
        return entry.normal.color(colorScheme).opacity(model.mode != .current ? 0.3 : 1)
    }
}
